package sock

import (
	"errors"
	"fmt"
	"io"
	"net"
	"time"
)

// Decimal to integer starting at &s[i0].
// Returns number, new offset, success.
func dtoi(s string, i0 int) (n int, i int, ok bool) {
	n = 0
	for i = i0; i < len(s) && '0' <= s[i] && s[i] <= '9'; i++ {
		n = n*10 + int(s[i]-'0')
	}
	if i == i0 {
		return 0, i, false
	}
	return n, i, true
}

// Parse IPv4 address (d.d.d.d).
func ParseIPv4(s string) (ip uint32, err error) {
	var p [4]byte
	i := 0
	for j := 0; j < 4; j++ {
		if i >= len(s) {
			// Missing octets.
			err = errors.New("Missing octets.")
			return
		}
		if j > 0 {
			if s[i] != '.' {
				err = errors.New("IP string is error")
				return
			}
			i++
		}
		var (
			n  int
			ok bool
		)
		n, i, ok = dtoi(s, i)
		if !ok || n > 0xFF {
			err = errors.New("IP string is error")
			return
		}
		p[j] = byte(n)
	}
	if i != len(s) {
		err = errors.New("IP string is error")
		return
	}
	ip = uint32(p[0])<<24 | uint32(p[1])<<16 | uint32(p[2])<<8 | uint32(p[3])
	return ip, nil
}

func IPPort2String(ip uint32, port uint16) string {
	return fmt.Sprintf("%d.%d.%d.%d:%d", ip>>24, ip>>16&0xff, ip>>8&0xff, ip&0xff, port)
}

func IP2String(ip uint32) string {
	return fmt.Sprintf("%d.%d.%d.%d", ip>>24, ip>>16&0xff, ip>>8&0xff, ip&0xff)
}

func CreateTcpListener(host string) (listener *net.TCPListener, err error) {
	tcpAddr, err := net.ResolveTCPAddr("tcp", host)
	if err != nil {
		return nil, err
	}

	listener, err = net.ListenTCP("tcp", tcpAddr)
	if err != nil {
		return nil, err
	}
	return
}

func CreateTcpConnect(host string) (conn *net.TCPConn, err error) {
	tcpAddr, err := net.ResolveTCPAddr("tcp", host)
	if err != nil {
		return nil, err
	}

	conn, err = net.DialTCP("tcp", nil, tcpAddr)
	if err != nil {
		return nil, err
	}

	//设置tcp心跳时间
	conn.SetKeepAlive(true)
	conn.SetKeepAlivePeriod(60 * time.Second)
	return
}

func CreateUdpConnect(address string) (conn *net.UDPConn, err error) {
	addr, err := net.ResolveUDPAddr("udp", address)
	if err != nil {
		return nil, err
	}
	conn, err = net.DialUDP("udp4", nil, addr)
	if err != nil {
		return nil, err
	}
	return conn, err
}

func CreateUdpListener(host string) (listener *net.UDPConn, err error) {
	udpAddr, err := net.ResolveUDPAddr("udp", host)
	if err != nil {
		return nil, err
	}

	listener, err = net.ListenUDP("udp", udpAddr)
	if err != nil {
		return nil, err
	}
	return
}

//func read(conn *net.TCPConn, data []byte) (int, error) {
//	return conn.Read(data)
//}

/*在指定的时间内,接收指定的字节数, 当没有错误发生时, num = len(data)
* 当有错误发生是num为实际接收的字节数
 */
func ReadTimeout(conn io.Reader, data []byte, mstimeout int) (num int, err error) {
	length := len(data)
	num = 0

	if tcpconn, ok := conn.(*net.TCPConn); ok {
		err = tcpconn.SetReadDeadline(time.Now().Add(time.Millisecond * time.Duration(mstimeout)))
		if err != nil {
			return
		}
		for num < length {
			n, err := tcpconn.Read(data[num:]) ///*read(tcpconn, data[num:])*/
			if err != nil {
				return num, err
			}
			if n > 0 {
				tcpconn.SetReadDeadline(time.Now().Add(time.Millisecond * time.Duration(mstimeout)))
				num += n
			}
		}
	} else {
		n := 0
		t := time.Now()
		for num < length && mstimeout > 0 {
			//log.Println(num, length)
			n, err = conn.Read(data[num:])
			if err != nil && err.Error() == "EOF" {
				return num, err
			}
			//log.Println(err, err.Error() != "EOF")
			if n == 0 {
				t1 := int(time.Now().Sub(t) / time.Millisecond)
				if t1 >= mstimeout {
					err = errors.New("timeout")
					break
				} else if t1+50 > mstimeout {
					time.Sleep(time.Duration(mstimeout-t1) * time.Millisecond)
				} else {
					time.Sleep(time.Millisecond * 50)
				}
				continue
			}
			num += n
		}
	}
	return
}

/*接收指定的字节数, 当没有错误发生时, num = len(data)
* 当有错误发生是num为实际接收的字节数
 */
func Read(conn io.Reader, data []byte) (num int, err error) {
	length := len(data)
	num = 0
	for num < length {
		n, err := conn.Read(data[num:])
		if err != nil {
			return num, err
		}
		num += n
	}
	return
}

func ReadLenString(conn *net.TCPConn) (str string, err error) {
	data := make([]byte, 2)
	n, err := Read(conn, data)
	if err != nil {
		n = 0
		return
	}
	l := uint16(data[0])<<8 | uint16(data[1])
	data = make([]byte, l)
	n, err = Read(conn, data)
	str = string(data[:n])
	return
}

func WriteLenString(conn io.Writer, str string) (n int, err error) {
	data := make([]byte, 2)
	l := len(str)
	data[0] = byte(uint16(l) >> 8)
	data[1] = byte(l)
	n, err = conn.Write(data)
	if err != nil {
		n = 0
		return
	}
	n, err = conn.Write([]byte(str))
	if err != nil {
		n = 2
		return
	}
	n += 2
	return
}
